//
//  MLLazyLoadTableView.m
//  TycoonBuy
//
//  Created by molon on 16/4/18.
//  Copyright © 2016年 molon. All rights reserved.
//

#import "MLLazyLoadTableView.h"
#import "MLKit.h"
#import "DefaultMLLazyLoadTableViewCell.h"

/**
 *  判断某些delegate方法是否被内部使用过
 *
 *  @param sel 方法名
 *
 *  @return 是否使用
 */
static BOOL _isInterceptedSelector(SEL sel)
{
    return (
            sel == @selector(tableView:cellForRowAtIndexPath:) ||
            sel == @selector(tableView:heightForRowAtIndexPath:) ||
            sel == @selector(tableView:didSelectRowAtIndexPath:) ||
            
            sel == @selector(tableView:willDisplayCell:forRowAtIndexPath:) ||
            
            sel == @selector(tableView:numberOfRowsInSection:)
            );
}


@interface _MLLazyLoadTableViewProxy : NSProxy
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(MLLazyLoadTableView *)interceptor;
@end

@implementation _MLLazyLoadTableViewProxy {
    id<NSObject> __weak _target;
    MLLazyLoadTableView * __weak _interceptor;
}

- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(MLLazyLoadTableView *)interceptor
{
    // -[NSProxy init] is undefined
    if (!self) {
        return nil;
    }
    
    NSAssert(target, @"target must not be nil");
    NSAssert(interceptor, @"interceptor must not be nil");
    
    _target = target;
    _interceptor = interceptor;
    
    return self;
}

- (BOOL)respondsToSelector:(SEL)aSelector
{
    NSAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
    NSAssert(_interceptor, @"interceptor must not be nil");
    
    return (_isInterceptedSelector(aSelector) || [_target respondsToSelector:aSelector]);
}

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    NSAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
    NSAssert(_interceptor, @"interceptor must not be nil");
    
    if (_isInterceptedSelector(aSelector)) {
        return _interceptor;
    }
    
    return [_target respondsToSelector:aSelector] ? _target : nil;
}

@end


@interface MLLazyLoadTableView()

/**
 *  最后一次调用的请求
 */
@property (nonatomic, strong) MolonRequestHelper *lastRequestHelper;

@end

@implementation MLLazyLoadTableView{
    BOOL _needLazyLoad;
    BOOL _lazyLoading;
    
    _MLLazyLoadTableViewProxy *_delegateProxy;
    _MLLazyLoadTableViewProxy *_dataSourceProxy;
}

- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
{
    self = [super initWithFrame:frame style:style];
    if (self) {
        self.scrollsToTop = YES;
        self.backgroundColor = [UIColor clearColor];
        
        //default lazy load cell
        _lazyLoadCell = [DefaultMLLazyLoadTableViewCell new];
        WEAKSELF
        [_lazyLoadCell setClickForRetryBlock:^{
            STRONGSELF
            [sSelf loadDataWithRefresh:NO];
        }];
    }
    return self;
}

- (instancetype)initWithRefreshSection:(NSInteger)refreshSection exceptTopRowCount:(NSInteger)exceptTopRowCount
{
    return [self initWithRefreshSection:refreshSection exceptTopRowCount:exceptTopRowCount lazyLoadCell:nil];
}

- (instancetype)initWithRefreshSection:(NSInteger)refreshSection exceptTopRowCount:(NSInteger)exceptTopRowCount lazyLoadCell:(MLLazyLoadTableViewCell*)lazyLoadCell
{
    self = [super initWithFrame:CGRectZero style:UITableViewStylePlain];
    if (self) {
        self.scrollsToTop = YES;
        self.backgroundColor = [UIColor clearColor];
        
        _refreshSection = refreshSection;
        _exceptTopRowCount = exceptTopRowCount;
        
        if (lazyLoadCell) {
            _lazyLoadCell = lazyLoadCell;
        }else{
            _lazyLoading = [DefaultMLLazyLoadTableViewCell new];
        }
        
        WEAKSELF
        [_lazyLoadCell setClickForRetryBlock:^{
            STRONGSELF
            [sSelf loadDataWithRefresh:NO];
        }];
    }
    return self;
}

- (void)dealloc
{
    super.delegate  = nil;
    super.dataSource = nil;
}

#pragma mark - setter
- (void)setLastRequestHelper:(MolonRequestHelper *)lastRequestHelper
{
    if (_lastRequestHelper) {
        //被取消的请求，molonapi会忽略它，不会执行requestError回调
        [MolonAPI cancelRequestHelper:_lastRequestHelper];
    }
    
    _lastRequestHelper = lastRequestHelper;
}

- (void)setDataSource:(id<UITableViewDataSource>)dataSource
{
    NSAssert(dataSource == nil, @"MLLazyLoadTableView uses proxyDataSource, not UITableView's dataSource property.");
}

- (void)setDelegate:(id<UITableViewDelegate>)delegate
{
    NSAssert(delegate == nil, @"MLLazyLoadTableView uses proxyDelegate, not UITableView's delegate property.");
}

- (void)setProxyDelegate:(id<MLLazyLoadTableViewDelegate>)proxyDelegate
{
    if (proxyDelegate == nil) {
        super.delegate = nil;
        _proxyDelegate = nil;
        _delegateProxy = nil;
    } else {
        _proxyDelegate = proxyDelegate;
        _delegateProxy = [[_MLLazyLoadTableViewProxy alloc] initWithTarget:proxyDelegate interceptor:self];
        super.delegate = (id<UITableViewDelegate>)_delegateProxy;
    }
}

- (void)setProxyDataSource:(id<MLLazyLoadTableViewDataSource>)proxyDataSource
{
    if (proxyDataSource == nil) {
        super.dataSource = nil;
        _proxyDataSource = nil;
        _dataSourceProxy = nil;
    } else {
        _proxyDataSource = proxyDataSource;
        _dataSourceProxy = [[_MLLazyLoadTableViewProxy alloc] initWithTarget:proxyDataSource interceptor:self];
        super.dataSource = (id<UITableViewDataSource>)_dataSourceProxy;
    }
}

#pragma mark - helper
- (void)loadDataWithRefresh:(BOOL)refresh
{
    void (^requestBlock)() = ^{
        //获取内容
        MolonRequestHelper *helper = self.requestBlock(self,_refreshing);
        NSAssert(helper&&[helper isKindOfClass:[MolonRequestHelper class]]&&helper.state==MolonRequestHelperStateRequesting, @"requestHelperBlock必须返回有效的请求对象");
        
        self.lastRequestHelper = helper;
    };
    
    if (refresh) {
        _refreshing = YES;
        _lazyLoadCell.status = MLLazyLoadCellStatusInit;
        
        //判断当前是否有数据，无的话就不需要懒加载
        if (_data.count<=0) {
            _needLazyLoad = NO;
        }
        
        requestBlock();
    }else{
        //如果有请求在记录中，也不触发
        if (!_lastRequestHelper) {
            
            requestBlock();
            
            //cell标记正在加载中
            _lazyLoadCell.status = MLLazyLoadCellStatusLoading;
        }
    }
}

- (NSInteger)rowCount
{
    return _data.count+_exceptTopRowCount+1;
}

- (NSInteger)lazyLoadIndex
{
    return [self rowCount]-1;
}

- (NSIndexPath*)lazyLoadIndexPath
{
    return [NSIndexPath indexPathForRow:[self lazyLoadIndex] inSection:_refreshSection];
}

#pragma mark - tableView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section==_refreshSection&&indexPath.row==[self lazyLoadIndex]) {
        return _lazyLoadCell;
    }
    
    return [_proxyDataSource tableView:tableView cellForRowAtIndexPath:indexPath];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section==_refreshSection&&indexPath.row==[self lazyLoadIndex]) {
        return [_lazyLoadCell preferredHeightWithMaxWidth:self.frame.size.width];
    }
    
    if (![_proxyDelegate respondsToSelector:@selector(tableView:heightForRowAtIndexPath:)]) {
        return self.rowHeight<=0?44.0f:self.rowHeight;
    }
    
    return [_proxyDelegate tableView:tableView heightForRowAtIndexPath:indexPath];
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section==_refreshSection&&indexPath.row==[self lazyLoadIndex]) {
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
        return;
    }
    
    if ([_proxyDelegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) {
        [_proxyDelegate tableView:tableView didSelectRowAtIndexPath:indexPath];
    }
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section==_refreshSection) {
        return [self rowCount];
    }
    
    return [_proxyDataSource tableView:tableView numberOfRowsInSection:section];
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section==_refreshSection&&indexPath.row==[self lazyLoadIndex]) {
        if (!_needLazyLoad) {
            return;
        }
        
        [self loadDataWithRefresh:NO];
    }
    
    if ([_proxyDelegate respondsToSelector:@selector(tableView:willDisplayCell:forRowAtIndexPath:)]) {
        [_proxyDelegate tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath];
    }
}

#pragma mark - outcall
- (void)enablePullDownRefresh
{
    //给自己添加下下拉刷新
    WEAKSELF
    [self enableRefreshingWithAction:^{
        STRONGSELF
        [sSelf doRefresh];
    } scrollToTopAfterEndRefreshing:YES];
}

- (void)reset
{
    //结束下拉刷新
    [self endRefreshing];
    
    self.lastRequestHelper = nil;
    _refreshing = NO;
    _data = nil;
    _needLazyLoad = NO;
    
    [self doReloadDataWithCompletion:nil];
}

- (void)doRefresh
{
    [self loadDataWithRefresh:YES];
}

- (void)requestCompletedWithRequestHelper:(MolonRequestHelper *)requestHelper success:(BOOL)success pageData:(ModelArray*)pageData loadedAll:(BOOL)loadedAll
{
    if (_lastRequestHelper&&![requestHelper isEqual:_lastRequestHelper]) {
        return; //不是最后投递的请求就直接忽略
    }
    self.lastRequestHelper = nil;
    
    void (^checkLazyLoadRightNowBlock)() = ^{
        //如果当前需要懒加载，而当前懒加载行已经处于显示状态，并且不是failed状态，就直接执行下一页请求
        if (_needLazyLoad&&_lazyLoadCell.status!=MLLazyLoadCellStatusLoadFailed) {
            NSArray *visibleIndexPaths = [self indexPathsForVisibleRows];
            if ([visibleIndexPaths containsObject:[NSIndexPath indexPathForRow:[self lazyLoadIndex] inSection:_refreshSection]]) {
                //保护措施，如果到这里懒加载行还是显示状态，就直接去找下一页
                [self loadDataWithRefresh:NO];
            }
        }
    };
    
    if (success) {
        NSAssert(loadedAll||pageData.count>0, @"pageData返回0个数据，但是没有标记loadedAll为YES");
        
        _needLazyLoad = !loadedAll;
        
        if (_refreshing) {
            _data = nil;
        }
        
        void (^completionBlock)() = ^{
            if (loadedAll) {
                _lazyLoadCell.status = MLLazyLoadCellStatusLoadedAll;
            }else{
                checkLazyLoadRightNowBlock();
            }
        };
        
        if (pageData.count>0) {
            NSMutableArray *indexes = nil;
            
            if (!_refreshing) {
                //在addObjects之间去设置这些indexes
                indexes = [NSMutableArray arrayWithCapacity:pageData.count];
                NSInteger currentRowCount = [self rowCount];
                for (NSUInteger i=0; i<pageData.count; i++) {
                    [indexes addObject:[NSIndexPath indexPathForRow:currentRowCount-1+i inSection:_refreshSection]];
                }
            }
            
            //添加到data里
            if (_data.count<=0) {
                _data = [NSClassFromString([NSString stringWithFormat:@"%@Array",[[pageData firstObject]class]]) new];
                NSAssert(_data,@"可能找不到对应懒加载Model的ModelArray");
            }
            
            [_data addObjectsFromArray:pageData.array];
            
            //添加到界面
            if (_refreshing) { //这里说明是刷新,直接reload当前列表
                [self doReloadDataWithCompletion:completionBlock];
            }else{
                [CATransaction setDisableActions:YES];
                [self beginUpdates];
                [self insertRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationNone];
                [self endUpdates];
                [CATransaction setDisableActions:NO];
                
                completionBlock();
            }
        }else{
            if (_refreshing) {
                NSAssert(loadedAll, @"发生异常，pageData数量为0，但loadedAll却不是YES");
                [self doReloadDataWithCompletion:^{
                    if (loadedAll) {
                        _lazyLoadCell.status = MLLazyLoadCellStatusEmpty;
                    }else{
                        checkLazyLoadRightNowBlock();
                    }
                }];
            }else{
                //如果到这里pageData数量为0，而且loadedAll不为YES的话很有可能是因为外部做了去重，而之前已经有的data里有完全重复的
                //这种情况下直接判断并请求下一页吧
                completionBlock();
            }
        }
    }else{
        //结束下拉刷新
        [self endRefreshing];
        
        if (_refreshing) {
            if (_data.count>0) {
                //刷新失败，但是原本已有数据，所以还是继续启用懒加载
                _needLazyLoad = YES;
            }
            
            //刷新失败，但是没准刷新请求的条件已经改变，这里可以做回归条件处理或者其他(例如reset)
            if (self.refreshFailedBlock) {
                self.refreshFailedBlock(self,requestHelper);
            }
            
            checkLazyLoadRightNowBlock();
        }else{
            _lazyLoadCell.status = MLLazyLoadCellStatusLoadFailed;
        }
    }
    
    _refreshing = NO;
}

- (void)doReloadDataWithCompletion:(void (^)())completion
{
    [self reloadData];
    
    if (completion) {
        completion();
    }
    
    if (self.afterReloadDataBlock) {
        self.afterReloadDataBlock(self);
    }
    
    [self endRefreshing];
}

@end
